Pinvon's Blog

所见, 所闻, 所思, 所想

JavaScript笔记--Cookie与Session

概述

HTTP 是一种无状态协议, 即, 当客户端发出请求的时候, 无法得知上一次请求的状态. 一旦数据交换完毕, 客户端与服务器的连接就会关闭, 再次交换数据需要建立新的连接.

如, 用户下单, 由于 HTTP 无状态, 服务器并不知道是哪个用户下的单.

于是产生了 Cookie 和 Session. Cookie 在客户端记录信息, 用于确定用户身份, Session 在服务器记录信息, 用于确定用户身份.

网上找到了一张图, 很清楚地解释了 Cookie 和 Session 的作用:

0.png

Cookie

可以简单理解为, Cookie 是服务器向客户端颁发的证书, 客户端用这个证书来证明自己的身份. 这样服务器就知道是哪个用户给自己发送了请求.

Cookie 笔记

Session

Session 是另一种记录用户状态的机制, 当客户端访问服务器的时候, 服务器就把客户端的信息通过 Session 记录在服务器上. 与 Cookie 不同, Session 保存在服务器. 保存的方式很多, 内存, 数据库, 文件, 都可以.

为什么有了 Cookie, 还要用 Session? 主要是因为 Cookie 保存在客户端, 数据很有可能被伪造, 所以重要的数据不能保存在 Cookie; 其次, 是因为 Cookie 中数据如果太多, 影响传输效率.

Session 的使用需要依赖于 Session id, 而 Session id 存在于 Cookie 中(在 Express 中默认是 connect.sid 属性), 所以, 可以说 Session 依赖于 Cookie. 但也不绝对, 如果浏览器禁用了 Cookie, 还可以通过其他方式实现 Session, 如在 url 中传递 session_id. 可以把 session_id 看成是客户端的唯一标识. 当请求到来时, 服务器检查 Cookie 中保存的 session_id, 然后通过这个 id 与服务器的 session data 关联起来, 进行数据的保存和修改.

具体来说, 当客户端发送请求到服务器时, 服务器会随机产生一个 1024bit 的字符串, 存在 Cookie 的 connect.sid 属性里. 当下次客户端再发送请求到服务器里时, 附带的 Cookie 中就会有这个字符串, 这样服务器就知道这个用户的身份了, 然后根据这个身份在服务器中取出相应的数据.

signedCookie

其实, 伪造 Cookie 是一件非常难的事, 因为有经验的开发人员, 都不会直接存储 Cookie, 他们会将 Cookie 进行签名(对内容生成摘要), 再存储.

如, 原本的 Cookie 长这样:

{dotcom_user: 'alsotang'}

然后我们设置一个秘密字符串, 根据这个秘密字符串, 对 Cookie 进行签名. 如:

{'dotcom_user.sig': '4850a42e3bc0d39c978770392cbd8dc2923e3d1d'}

现在, 如果浏览器更改了 Cookie 中的信息, 服务器就会发现 hash 校验结果不同.

在 Express 中使用

使用 Cookie

cookie-parser

npm install cookie-parser

使用

例 1
var express = require('express');

// 可以直接使用req.cookies.cname来访问cookie
var cookie = require('cookie-parser');

// 创建app对象
var app = express();
app.use(cookie())

// 设置cookie
app.get('/setc',function(req,res){
    res.cookie('resc', '设置到cookie里的值', { expires: new Date(Date.now() + 900000), httpOnly: true });
    res.end('cookies set ok')
})

// 取得cookie
app.get('/getc',function(req,res){
    console.log('取得的cookie:'+req.cookies.resc)
    res.end('cookies get ok')
})

// 在3000端口监听http请求
app.listen(3000);
例 2
var express = require('express');
// 首先引入 cookie-parser 这个模块
var cookieParser = require('cookie-parser');

var app = express();
app.listen(3000);

// 使用 cookieParser 中间件,cookieParser(secret, options)
// 其中 secret 用来加密 cookie 字符串(下面会提到 signedCookies)
// options 传入上面介绍的 cookie 可选参数
app.use(cookieParser());

app.get('/', function (req, res) {
  // 如果请求中的 cookie 存在 isVisit, 则输出 cookie
  // 否则,设置 cookie 字段 isVisit, 并设置过期时间为1分钟
  if (req.cookies.isVisit) {
    console.log(req.cookies);
    res.send("再次欢迎访问");
  } else {
    res.cookie('isVisit', 1, {maxAge: 60 * 1000});
    res.send("欢迎第一次访问");
  }
});

使用 Session

express-session

express-session 中间件默认将 Session 存放到内存, 我们也可以设置使用其他方式存储.

Session 的参数

  • name: 设置 Cookie 中保存 Session 的字段名, 默认为 connect.sid
  • store: Session 的存储方式, 默认为内存
  • secret: 计算 secret 对应的 hash 值, 放在 Cookie 中, 使产生的 signedCookie 防篡改
  • cookie: 设置存放 session id 的 Cookie 的相关选项, 默认为 (default: { path: '/', httpOnly: true, secure: false, maxAge: null })
  • genid: 产生新 session id 时使用的函数
  • rolling: 为每个请求都重新设置 Cookie, 默认为 false
  • resave: 即使 session 未修改, 也重新保存 session 值, 默认为 true

例 1

var express = require('express');
// 首先引入 express-session 这个模块
var session = require('express-session');

var app = express();
app.listen(5000);

// 按照上面的解释,设置 session 的可选参数
app.use(session({
  secret: 'recommand 128 bytes random string', // 建议使用 128 个字符的随机字符串
  cookie: { maxAge: 60 * 1000 }
}));

app.get('/', function (req, res) {

  // 检查 session 中的 isVisit 字段
  // 如果存在则增加一次,否则为 session 设置 isVisit 字段,并初始化为 1。
  if(req.session.isVisit) {
    req.session.isVisit++;
    res.send('<p>第 ' + req.session.isVisit + '次来此页面</p>');
  } else {
    req.session.isVisit = 1;
    res.send("欢迎第一次来这里");
    console.log(req.session);
  }
});

例 2

// session功能
var session = require('express-session');

// secret是设置在cookie里的sessionkey
app.use(session({'secret':'WBLOG'})) 

// 设置session
app.get('/login',function(req,res){
    req.session.user = {
        'name':req.query.name,
        'pass':req.query.pass,
        'avatar':req.query.avatar
    }
    res.end('session is OK.');
})

使用 mongodb 存储 Session

var MongoStore  = require('connect-mongo')(session);
app.use(session({
    'secret':'WBLOG',
    'store':new MongoStore({
        'db':'esession',
        'host': 'localhost',
        'port': 27017,  
        'collection':'sessions',// 存在哪个集合里,默认为sessions   
        'ttl':10, // session过期时间
        'autoRemove': 'native',// mongo2.2+自动移除过期的session,disable为禁用
        'autoRemoveInterval': 10, //移除过期session间隔时间,默认为10分钟
        'touchAfter': 24 * 3600 //同步session间隔,默认每次请求都会同步到数据库
    })
})) 
// secret是设置在cookie里的sessionkey
// store用来指定session存储位置

Comments

使用 Disqus 评论
comments powered by Disqus